﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

using uno.util;
using unoidl.com.sun.star.beans;
using unoidl.com.sun.star.bridge;
using unoidl.com.sun.star.frame;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;

namespace Office.Lib
{
    public class OpenOfficeHelper// : XEventListener// IOpenOffice
    {

        // For thread safety
        private Mutex _openOfficeLock;

        /// <summary>
        /// constructor
        /// </summary>
        public OpenOfficeHelper()
        {
            _openOfficeLock = new Mutex(false, "OpenOfficeMutexLock-MiloradCavic");
        }
        private readonly string _connectionString;
        private XComponent _bridgeComponent;
        private XMultiComponentFactory _serviceManager;
        private XComponentContext _componentContext;
        private XBridge _bridge;
        private bool _connected;
        private bool _expectingDisconnection;


        /// <summary>
        /// Converts document to PDF
        /// </summary>
        /// <param name="sourcePath">Path to document to convert(e.g: C:\test.doc)</param>
        /// <param name="destinationPath">Path on which to save PDF (e.g: C:\test.pdf)</param>
        /// <returns>Path to destination file if operation is successful, or Exception text if it is not</returns>

//        public static extern void bootstrap();
        public void ConvertDocToPDF(string sourcePath, string destinationPath)
        {
            bool obtained = _openOfficeLock.WaitOne(60 * 1000, false);
            InitUno();
            //  SocketUtils.Connect();
            XComponent xComponent = null;
            try
            {
                if (!obtained)
                {
                    throw new System.Exception(string.Format("Request for using OpenOffice wasn't served after {0} seconds. Aborting...", 30));
                }

                sourcePath = PathConverter(sourcePath);
                destinationPath = PathConverter(destinationPath);

                // 载入文件前属性设定，设定文件开启时隐藏
                //PropertyValue[] loadDesc = new PropertyValue[1];
                //loadDesc[0] = new PropertyValue();
                //loadDesc[0].Name = "Hidden";
                //loadDesc[0].Value = new uno.Any(true);
                //   XComponentContext localContext = Bootstrap.bootstrap();
                //XComponentContext localContext = Bootstrap.defaultBootstrap_InitialComponentContext();
                //XMultiComponentFactory localServiceManager = localContext.getServiceManager();
                //XConnector connector = (XConnector)localServiceManager.createInstanceWithContext("com.sun.star.connection.Connector", localContext);
                //XConnection connection = connector.connect(_connectionString);
                //XBridgeFactory bridgeFactory = (XBridgeFactory)localServiceManager.createInstanceWithContext("com.sun.star.bridge.BridgeFactory", localContext);
                //_bridge = bridgeFactory.createBridge("", "urp", connection, null);
                //_bridgeComponent = (XComponent)_bridge;
                //_bridgeComponent.addEventListener(this);
                //_serviceManager = (XMultiComponentFactory)_bridge.getInstance("StarOffice.ServiceManager");
                //XPropertySet properties = (XPropertySet)_serviceManager;
                //// Get the default context from the office server. 
                //var oDefaultContext = properties.getPropertyValue("DefaultContext");

                //_componentContext = (XComponentContext)oDefaultContext.Value;
                //_connected = true;
                ////Get a ComponentContext
                unoidl.com.sun.star.uno.XComponentContext xLocalContext = Bootstrap.bootstrap();// Bootstrap.bootstrap();

                //Get MultiServiceFactory
                //传递给组件的组件上下文。
                unoidl.com.sun.star.lang.XMultiServiceFactory xRemoteFactory = (XMultiServiceFactory)xLocalContext.getServiceManager();

                //Get a CompontLoader

                //这是一个简单的接口，用于通过 URL 将组件加载到框架环境中
                XComponentLoader aLoader = (XComponentLoader)xRemoteFactory.createInstance("com.sun.star.frame.Desktop");

                //Load the sourcefile
                xComponent = aLoader.loadComponentFromURL(sourcePath, "_blank", 0, new unoidl.com.sun.star.beans.PropertyValue[0]);

                //Wait for loading
                while (xComponent == null)
                {
                    Thread.Sleep(3000);
                }

                SaveDocument(xComponent, destinationPath);

                xComponent.dispose();

            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                Process[] pt = Process.GetProcessesByName("soffice.bin");
                if (pt != null && pt.Length > 0)
                {
                    foreach (var item in pt)
                    {
                        item.Kill();
                    }
                }
                if (obtained)
                {
                    _openOfficeLock.ReleaseMutex();
                }
            }
        }

        /// <summary>
        /// 执行保存
        /// </summary>
        /// <param name="xComponent">The x component.</param>
        /// <param name="filePath">Name of the file.</param>
        private void SaveDocument(XComponent xComponent, [In] string filePath)
        {
            unoidl.com.sun.star.beans.PropertyValue[] propertyValue = new unoidl.com.sun.star.beans.PropertyValue[1];
          
            propertyValue[0] = new unoidl.com.sun.star.beans.PropertyValue();
            propertyValue[0].Name = "FilterName";
            propertyValue[0].Value = new uno.Any("writer_pdf_Export");

            ((XStorable)xComponent).storeToURL(filePath, propertyValue);
        }

        /// <summary>
        /// Convert into OO file format
        /// </summary>
        /// <param name="file">The file.</param>
        /// <returns>The converted file</returns>
        private static string PathConverter(string file)
        {
            try
            {
                file = file.Replace(@"\", "/");

                return "file:///" + file;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        public void disposing(EventObject Source)
        {
            throw new NotImplementedException();
        }
        public static void InitUno()
        {
            var info = Get();
            if (info.Kind == OfficeKind.LibreOffice)
            {
                Environment.SetEnvironmentVariable("UNO_PATH", info.OfficeUnoPath, EnvironmentVariableTarget.Process);
                Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + @";" + info.OfficeUnoPath, EnvironmentVariableTarget.Process);
            }

        }
        public static OfficeInfo Get()
        {
            String unoPath = "";
            var libreOfficeSubKeyName = @"SOFTWARE\LibreOffice\UNO\InstallPath";
            var openOfficeSubKeyName = @"SOFTWARE\OpenOffice\UNO\InstallPath";

            // access 32bit registry entry for latest LibreOffice for Current User
            Microsoft.Win32.RegistryKey hkcuView32 = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.CurrentUser, Microsoft.Win32.RegistryView.Registry64);//Microsoft.Win32.RegistryView.Registry32
            Microsoft.Win32.RegistryKey hkcuUnoInstallPathKey = hkcuView32.OpenSubKey(libreOfficeSubKeyName, false);

            // access 32bit registry entry for latest LibreOffice for Local Machine (All Users)
            Microsoft.Win32.RegistryKey hklmView32 = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Registry64);//Microsoft.Win32.RegistryView.Registry32
            Microsoft.Win32.RegistryKey hklmUnoInstallPathKey = hklmView32.OpenSubKey(libreOfficeSubKeyName, false);
            if (hkcuUnoInstallPathKey != null && hkcuUnoInstallPathKey.ValueCount > 0)
            {
                unoPath = (string)hkcuUnoInstallPathKey.GetValue(hkcuUnoInstallPathKey.GetValueNames()[hkcuUnoInstallPathKey.ValueCount - 1]);
            }
            else
            {

                if (hklmUnoInstallPathKey != null && hklmUnoInstallPathKey.ValueCount > 0)
                {
                    unoPath = (string)hklmUnoInstallPathKey.GetValue(hklmUnoInstallPathKey.GetValueNames()[hklmUnoInstallPathKey.ValueCount - 1]);
                }
            }
            if (string.IsNullOrEmpty(unoPath))
            {
                hkcuUnoInstallPathKey = hkcuView32.OpenSubKey(openOfficeSubKeyName, false);
                hklmUnoInstallPathKey = hklmView32.OpenSubKey(openOfficeSubKeyName, false);
                if (hkcuUnoInstallPathKey != null && hkcuUnoInstallPathKey.ValueCount > 0)
                {
                    unoPath = (string)hkcuUnoInstallPathKey.GetValue(hkcuUnoInstallPathKey.GetValueNames()[hkcuUnoInstallPathKey.ValueCount - 1]);
                }
                else
                {

                    if (hklmUnoInstallPathKey != null && hklmUnoInstallPathKey.ValueCount > 0)
                    {
                        unoPath = (string)hklmUnoInstallPathKey.GetValue(hklmUnoInstallPathKey.GetValueNames()[hklmUnoInstallPathKey.ValueCount - 1]);
                    }
                }
            }

            var officeInfo = new OfficeInfo
            {
                OfficeUnoPath = unoPath,
                Kind = string.IsNullOrEmpty(unoPath) ? OfficeKind.Unknown : (unoPath.Contains("LibreOffice") ? OfficeKind.LibreOffice : OfficeKind.OpenOffice)
            };
            return officeInfo;
        }
    }

    public class OfficeInfo
    {
        public string OfficeUnoPath { get; set; }
        public OfficeKind Kind { get; set; }
    }
    public enum OfficeKind
    {
        LibreOffice = 0,
        OpenOffice = 1,
        Unknown = 2,
    }
}
